SPB Git

Repository Public

page.tsx 2.9 KB 46 lines tsx

1// Vrai-Prix — méthode du coût : fiche d'un assemblage.2import type { Metadata } from "next";3import { notFound } from "next/navigation";4import AssemblyDetail, { type AssemblyDetailData } from "@/components/cout/AssemblyDetail";5import { buildContext, loadLocations } from "@/lib/cost/catalog";6import { assemblyUnitCost } from "@/lib/cost/engine";7import { cached } from "@/lib/cost/cache";89export const dynamic = "force-dynamic";1011function load(code: string): AssemblyDetailData | null {12  return cached(`assembly-page:${code}`, 10 * 60_000, () => {13    const ctx = buildContext({ locationCode: "QC-MTL" });14    const a = ctx.assemblies.get(code);15    if (!a) return null;16    const unit = assemblyUnitCost(a, ctx);17    const regional = loadLocations().map((l) => ({ code: l.code, nameFr: l.nameFr, nameEn: l.nameEn, direct: Math.round(100 * (unit.material * l.materialFactor + unit.labour * l.labourFactor + unit.equipment * l.equipmentFactor)) / 100, materialFactor: l.materialFactor, labourFactor: l.labourFactor, equipmentFactor: l.equipmentFactor }));18    const history: AssemblyDetailData["history"] = [];19    const now = new Date(ctx.asOf);20    for (let k = 7; k >= 0; k--) {21      const d = new Date(now); d.setMonth(d.getMonth() - 3 * k);22      const asOf = d.toISOString().slice(0, 10);23      const c2 = k === 0 ? ctx : buildContext({ asOf, locationCode: "QC-MTL" });24      const u2 = assemblyUnitCost(a, c2);25      const kinds = new Set(u2.components.filter((c) => c.itemCode).map((c) => c.provenance.kind));26      history.push({ date: asOf, direct: u2.direct, material: u2.material, labour: u2.labour, equipment: u2.equipment, kind: kinds.has("observed") || kinds.has("official") ? "observed" : kinds.has("indexed") ? "indexed" : "reference" });27    }28    const neighbours = [...ctx.assemblies.values()].filter((x) => x.category === a.category && x.code !== a.code).slice(0, 8).map((x) => { const u = assemblyUnitCost(x, ctx); return { code: x.code, nameFr: x.nameFr, nameEn: x.nameEn, unit: x.unit, direct: u.direct }; });29    return { asOf: ctx.asOf, location: ctx.location, assembly: a, unit, regional, history, neighbours };30  });31}3233export async function generateMetadata({ params }: { params: Promise<{ code: string }> }): Promise<Metadata> {34  const { code } = await params;35  const d = load(decodeURIComponent(code));36  if (!d) return { title: "Assemblage introuvable" };37  return { title: `${d.assembly.nameFr} — ${d.unit.direct.toLocaleString("fr-CA", { maximumFractionDigits: 2 })} $ / ${d.assembly.unit} · assemblage`, description: d.assembly.descriptionFr, alternates: { canonical: `/cout/assemblage/${encodeURIComponent(d.assembly.code)}` } };38}3940export default async function AssemblyPage({ params }: { params: Promise<{ code: string }> }) {41  const { code } = await params;42  const d = load(decodeURIComponent(code));43  if (!d) notFound();44  return <AssemblyDetail data={d} />;45}46
Files main
Menu

Theme